#include "Stick.h"

#include "Common.h"

#include<iostream>
#include<cstdlib>
#include<math.h>

const int SURFACES = 32;
const double PI = 3.14159265;
const int RELEASE_TIME = 30; // ticks
const int PAUSE_TIME = 0;

Stick::Stick(CueBall* _cueBall) : state(READY), releaseTime(0), pullBackPercent(0), xOffset(0), yOffset(0)
{
   setColor(Vec3f(0,0,1));
   
   cueBall = _cueBall;
   length = Config::getStickLength();
   tipDiameter = Config::getStickTipDiameter();
   endDiameter = Config::getStickEndDiameter();
   
   points = new Point*[SURFACES];
   double theta = 2 * PI / SURFACES;
   for (int i = 0; i < SURFACES; i++)
   {
      double angle = i * theta;  
      points[i] = new Point(cos(angle), sin(angle));
   } 
   
   setAngle(0);
}

Stick::~Stick()
{
   if (points) delete [] points;
}

Vec3f Stick::getOrigin() 
{
   Vec3f c = cueBall->getInitialPosition();
   return Vec3f(c[0] + getXOffset(), c[1] + getYOffset(), c[2]);
}

double Stick::getXOffset()
{
   return xOffset * cueBall->getRadius();
}
double Stick::getYOffset()
{
   return yOffset * cueBall->getRadius();
}
void Stick::moveXOffset(double change)
{
   xOffset += change;
   
   double maxX = sqrt(1 - yOffset * yOffset);
   if (xOffset < -maxX)
      xOffset = -maxX;
   else if (xOffset > maxX)
      xOffset = maxX;
}
void Stick::moveYOffset(double change)
{
   double maxY = sqrt(1 - xOffset * xOffset);   
   
   yOffset += change;
   if (yOffset < -maxY)
      yOffset = -maxY;
   else if (yOffset > maxY)
      yOffset = maxY;
}

void Stick::drawShadow(Vec3f pos, Vec3f dif, int index)
{
   //draw(false);
   return;
   
   glPushMatrix();

   Vec3f o = getOrigin();
   
   makeRotations();
   

   glBegin(GL_QUADS);
   //glNormal3f(+0.0f, +1.0f,+0.0f);
   glColor4f(0,0,0,1);
   glVertex3f(o[0], 1, o[2]);
   glVertex3f(o[0], 1, o[2]+500);
   glVertex3f(o[0]+500, 1, o[2]+500);
   glVertex3f(o[0]+500, 1, o[2]);
   glEnd();

   
//   double xChange = origin[0] - light[0];
//   double yChange = light[1] - origin[1] - radius;
//   double xyTheta = atan(xChange / yChange);
//   double xyThetaC = atan(radius / sqrt(xChange*xChange + yChange*yChange));
//   double xLeft = light[1] * tan(xyTheta - xyThetaC);
//   double xRight = light[1] * tan(xyTheta + xyThetaC);
//   
//   double zChange = origin[2] - light[2];
//   double zyTheta = atan(zChange / yChange);
//   double zyThetaC = atan(radius / sqrt(zChange*zChange + yChange*yChange));
//   double zFront = light[1] * tan(zyTheta - zyThetaC);
//   double zBack = light[1] * tan(zyTheta + zyThetaC);
//   
//   glBegin(GL_QUADS);
//   //glNormal3f(+0.0f, +1.0f,+0.0f);
//   glColor3f(0,0,0);
//   glVertex3f(xLeft, 1, zBack);
//   glVertex3f(xLeft, 1, zFront);
//   glVertex3f(xRight, 1, zFront);
//   glVertex3f(xRight, 1, zBack);
//   glEnd();
   
   glPopMatrix();
}

void Stick::makeRotations()
{
   Vec3f c = cueBall->getInitialPosition();
   glTranslatef(c[0], c[1], c[2]);
   glRotatef(hAngle, 0, 1, 0);   
   glRotatef(-vAngle, 1, 0, 0);
   
   glTranslatef(getXOffset(), cueBall->getRadius() + getYOffset(), cueBall->getRadius());  // move to center of ball
   
   
   int time = (int)(Time::getTickCount() - releaseTime); // ticks gone through
   
   if (state == READY)
   {
      glTranslatef(0, 0, cueBall->getDiameter()); // bring back a ball diameter from cue ball
   }
   else if (state == HELD)
   {
      glTranslatef(0, 0, pullBackPercent * length / 2); // bring back up to 1/2 the length
   }
   else if (state == RELEASED)
   {      
      if (time < RELEASE_TIME) // still being shot
      {
         int timeLeft = RELEASE_TIME - time;
         int pullBackLength = (int)((timeLeft / (double) RELEASE_TIME) * (pullBackPercent * length));
         glTranslatef(0, 0, pullBackLength); 
      } 
      else // Hits the cue ball
      {
         double velocity = pullBackPercent * 20.0;
         Force f(hAngle + 180, velocity);
         cueBall->setForce(f);
         state = PAUSED;
      }
   } 
   else if (state == PAUSED && time > RELEASE_TIME + PAUSE_TIME)
   {
      //state = HIDDEN;
      state = READY;
   }   
}

void Stick::draw(bool ortho, bool movingBalls)
{   
  if (movingBalls)
     return;
   
   IDrawableObject::draw(ortho);   
   glPushMatrix();

   glPushName(200); /* Overwrite the first name in the buffer */
         
   makeRotations();
   
   glBegin(GL_QUAD_STRIP);
   for (int i = 0; i < SURFACES; i++)
   {
      glNormal3f(points[i]->x * tipDiameter / 2, points[i]->y * tipDiameter / 2, 0);
      glVertex3f(points[i]->x * tipDiameter / 2, points[i]->y * tipDiameter / 2, 0);
      glVertex3f(points[i]->x * endDiameter / 2, points[i]->y * endDiameter / 2, length);
   }
   glNormal3f(points[0]->x * tipDiameter / 2, points[0]->y * tipDiameter / 2, 0);
   glVertex3f(points[0]->x * tipDiameter / 2, points[0]->y * tipDiameter / 2, 0);
   glVertex3f(points[0]->x * endDiameter / 2, points[0]->y * endDiameter / 2, length);
   glEnd(); 
   
   glPopName();
   glPopMatrix();
}

void Stick::setAngle(double _hAngle)
{
   hAngle = _hAngle;
   double lengthA = -1;
   double lengthB = -1;

   Vec3f o = getOrigin();
   
   
   
   double r = (Config::getTableDepth() / 2) - o[2];
   double t = (Config::getTableWidth() / 2) - o[0];
   double l = Config::getTableDepth() - r;
   double b = Config::getTableWidth() - t;

   double angle = hAngle * PI / 180.0;

   if (angle < PI / 2.0)
   {
      lengthA = r / cos(angle);
      if (angle != 0)
         lengthB = t / sin(angle);
   } 
   else if (angle < PI)
   {
      if (angle != PI / 2.0)
         lengthA = -l / cos(angle);
      lengthB = t / sin(angle);      
   }
   else if (angle < 1.5 * PI)
   {
      lengthA = -l / cos(angle);
      if (angle != PI)
         lengthB = -b / sin(angle);         
   }
   else
   {
      if (angle != 1.5 * PI)
         lengthA = r / cos(angle);
      lengthB = -b / sin(angle);        
   }
   
   double length = lengthA;
   if (lengthB > 0 && lengthB < lengthA || lengthA < 0)
      length = lengthB;

   vAngle = atan(Config::getTableHeight() / length) * 180 / PI; 
}

void Stick::setPullBackPercent(double _percent)
{
   if (state == READY || state == HELD)
   {
      state = HELD;
      pullBackPercent = _percent;      
   }
}

void Stick::release()
{
   if (state == HELD)
   {
      releaseTime = Time::getTickCount();     
      state = RELEASED;
   }

}

Vec3f Stick::getVector()
{
   double angle = hAngle * PI / 180.0 + PI;
   return Vec3f(sin(angle), 0, cos(angle));
}